﻿using Athena.Http;
using BuddHa.Camera.Options;
using BuddHa.Camera.Protcol.HK.Models;
using BuddHa.Common.JsonOpex;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace BuddHa.Camera.Protcol.HK
{
    public class HkHttpProtcol
    {
        public const string ClientName = "hk_http1.1";

        protected ILogger Logger { get; }

        protected IJsonOpexSerializer _jsonOpexSerializer { get; }

        protected IHttpRequester HttpRequester { get; }

        protected CameraOptions _cameraOptions { get; set; }

        public HkHttpProtcol(IJsonOpexSerializer jsonOpexSerializer, ILogger<HkHttpProtcol> logger, IOptions<CameraOptions> options, IHttpRequester httpRequester)
        {
            _jsonOpexSerializer = jsonOpexSerializer;
            Logger = logger;
            HttpRequester = httpRequester;
            _cameraOptions = options.Value;
        }
        public async Task<TResponse> HkHttpPost<TResponse>(string uri, object body) where TResponse : BaseCoreResponse, new()
        {
            return await HkHttpPost<TResponse>(uri, JsonSerializer.Serialize(body));
        }


        public async Task<TResponse> HkHttpPost<TResponse>(string uri, string body) where TResponse : BaseCoreResponse, new()
        {
            var header = new Dictionary<string, string>();
            InitRequestHeader(header, uri, body, true, _cameraOptions.HkConfig.AppKey, _cameraOptions.HkConfig.AppSecret);
            var sbUri = new StringBuilder();
            sbUri.Append(_cameraOptions.HkConfig.Gateway).Append(uri);
            var realHeader = new Dictionary<string, string>();
            foreach (string key in header.Keys)
            {
                if (key == "Accept" || key.Contains("x-ca-"))
                {
                    realHeader.Add(key, header[key]);
                }
            }
            var strResponse = string.Empty;
            try
            {
                var sw = new Stopwatch();
                sw.Start();
                strResponse = await HttpRequester.PostAsync(sbUri.ToString(), body, ClientName, "", realHeader,
                (reqHeader) =>
                {
                    reqHeader.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                },
                (reqMes) =>
                {
                    reqMes.Version = HttpVersion.Version11;
                });
                sw.Stop();
                var tResponse = _jsonOpexSerializer.Deserialize<TResponse>(strResponse);
                //var quartzHttpLogAdapter = new QuartzHttpLogAdapter();
                //await quartzHttpLogAdapter.InsertAsync(new Adapter.Models.QuartzHttpLog()
                //{
                //    GuidRequest = Guid.NewGuid().ToString("N"),
                //    Uri = uri,
                //    RequestBody = body.Length > 255 ? body.Substring(0, 255) : body,
                //    ResponseBody = strResponse.Length > 255 ? strResponse.Substring(0, 255) : strResponse,
                //    ElapsedMilliseconds = sw.ElapsedMilliseconds,
                //    TimeCreate = DateTime.Now
                //});
                return tResponse;
            }
            catch (Exception ex)
            {
                //var quartzHttpLogAdapter = new QuartzHttpLogAdapter();
                //await quartzHttpLogAdapter.InsertAsync(new Adapter.Models.QuartzHttpLog()
                //{
                //    GuidRequest = Guid.NewGuid().ToString("N"),
                //    Uri = uri,
                //    RequestBody = body.Length > 255 ? body.Substring(0, 255) : body,
                //    ResponseBody = strResponse.Length > 255 ? strResponse.Substring(0, 255) : strResponse,
                //    ElapsedMilliseconds = 0,
                //    TimeCreate = DateTime.Now
                //});
                return new TResponse()
                {
                    Code = "601",
                    Msg = ex.Message
                };
            }
        }

        public void InitRequestHeader(Dictionary<string, string> header, string url, string body, bool isPost, string appKey, string secret)
        {
            string accept = "application/json";
            header.Add("Accept", accept);

            string contentType = "application/json";
            header.Add("Content-Type", contentType);

            if (isPost)
            {
                string contentMd5 = ComputeContentMd5(body);
                header.Add("content-md5", contentMd5);
            }

            string timestamp = CameraTimeProcess.GetHkTimeStamp().ToString();
            header.Add("x-ca-timestamp", timestamp);

            string nonce = Guid.NewGuid().ToString();
            header.Add("x-ca-nonce", nonce);

            header.Add("x-ca-key", appKey);

            string strToSign = BuildSignString(isPost ? "POST" : "GET", url, header);
            string signedStr = ComputeForHMACSHA256(strToSign, secret);
            header.Add("x-ca-signature", signedStr);
        }

        /// <summary>
        /// 计算HMACSHA265
        /// </summary>
        /// <param name="str">待计算字符串</param>
        /// <param name="secret">平台APPSecet</param>
        /// <returns>HMAXH265计算结果字符串</returns>
        private static string ComputeForHMACSHA256(string str, string secret)
        {
            var encoder = new System.Text.UTF8Encoding();
            byte[] secretBytes = encoder.GetBytes(secret);
            byte[] strBytes = encoder.GetBytes(str);
            var opertor = new HMACSHA256(secretBytes);
            byte[] hashbytes = opertor.ComputeHash(strBytes);
            return Convert.ToBase64String(hashbytes);
        }

        /// <summary>
        /// 计算content-md5
        /// </summary>
        /// <param name="body"></param>
        /// <returns>base64后的content-md5</returns>
        private string ComputeContentMd5(string body)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = md5.ComputeHash(System.Text.Encoding.UTF8.GetBytes(body));
            return Convert.ToBase64String(result);
        }

        private string BuildSignString(string method, string url, Dictionary<string, string> header)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(method.ToUpper()).Append("\n");
            if (null != header)
            {
                if (null != header["Accept"])
                {
                    sb.Append((string)header["Accept"]);
                    sb.Append("\n");
                }

                if (header.Keys.Contains("Content-MD5") && null != header["Content-MD5"])
                {
                    sb.Append((string)header["Content-MD5"]);
                    sb.Append("\n");
                }

                if (null != header["Content-Type"])
                {
                    sb.Append((string)header["Content-Type"]);
                    sb.Append("\n");
                }

                if (header.Keys.Contains("Date") && null != header["Date"])
                {
                    sb.Append((string)header["Date"]);
                    sb.Append("\n");
                }
            }

            // build and add header to sign
            string signHeader = BuildSignHeader(header);
            sb.Append(signHeader);
            sb.Append(url);
            return sb.ToString();
        }

        /// <summary>
        /// 计算签名头
        /// </summary>
        /// <param name="header">请求头</param>
        /// <returns>签名头</returns>
        private string BuildSignHeader(Dictionary<string, string> header)
        {
            Dictionary<string, string> sortedDicHeader = new Dictionary<string, string>();
            sortedDicHeader = header;
            var dic = from objDic in sortedDicHeader orderby objDic.Key ascending select objDic;

            StringBuilder sbSignHeader = new StringBuilder();
            StringBuilder sb = new StringBuilder();
            foreach (KeyValuePair<string, string> kvp in dic)
            {
                if (kvp.Key.Replace(" ", "").Contains("x-ca-"))
                {
                    sb.Append(kvp.Key + ":");
                    if (!string.IsNullOrWhiteSpace(kvp.Value))
                    {
                        sb.Append(kvp.Value);
                    }
                    sb.Append("\n");
                    if (sbSignHeader.Length > 0)
                    {
                        sbSignHeader.Append(",");
                    }
                    sbSignHeader.Append(kvp.Key);
                }
            }

            header.Add("x-ca-signature-headers", sbSignHeader.ToString());

            return sb.ToString();
        }

        private const string _successCode = "0";

        public bool CheckResponse(BaseCoreResponse baseCoreResponse, string explain)
        {
            if (baseCoreResponse.Code != _successCode)
            {
                var reason = !string.IsNullOrWhiteSpace(baseCoreResponse.Msg) ? baseCoreResponse.Msg : baseCoreResponse.Code;
                ShowHkErrorMssage(baseCoreResponse.Code, reason, explain);
                return false;
            }
            return true;
        }

        public void ShowHkErrorMssage(string code, string reason, string explain)
        {
            Logger.LogError($"{explain} Code : {code} Reason : {reason}");
        }
    }
}
